package laboratoryQ;

import java.lang.reflect.Field;
import java.util.*;

import laboratoryQ.experiment.settings.IExperimentSettings;
import laboratoryQ.computation.IRandom;

public class BaseUtils {
	@SuppressWarnings("unchecked")
	public static List<Field> listFields(Class clazz) {
		Set<Field> fieldsSet = new HashSet<Field>();
		for (Class cl = clazz; cl != null; cl = cl.getSuperclass()) {
			for (Field f : cl.getDeclaredFields()) {
				f.setAccessible(true);
				fieldsSet.add(f);
			}
		}
		return new ArrayList<Field>(fieldsSet);
	}

	public static Object safeGetFieldVal(Field field, Object obj) {
		try {
			return field.get(obj);
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

	public static void safeSetFieldVal(Field field, IExperimentSettings obj, Object value) {
		try {
			field.set(obj, value);
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static int randomInt(IRandom r, int max) {
		if (max <= 0) {
			return 0;
		}
		double d = r.get() * max * 1e5;
		int di = (int) d;
		return di % max;
	}

	public static Object randomElement(
			IRandom random,
			Object[] arr
	) {
		int n = arr.length;
		n = randomInt(random, n);
		return arr[n];
	}
	
	public static double randomFromDist(IRandom r, double beg, double end) {
		return r.get() * (end - beg) + beg;
	}
	
	public static int weightedRandom(double[] buf, IRandom random) {
		return waightedRandom(buf, random, 1e-5);
	}

	public static int waightedRandom(double[] buf, IRandom random, double EPS) {
		double sum = 0.0;
		int n = buf.length;
		for (int i = 0; i < n; i++) {
			sum += buf[i];
		}
		double choise = sum * random.get();
		for (int i = 0; i < n; i++) {
			choise -= buf[i];
			if (choise < EPS) {
				return i;
			}
		}
		return n - 1;
	}

	public static int cycleNext(int pos, int length) {
		pos++;
		if (pos == length) {
			return 0;
		}
		return pos;
	}

	public static int cyclePrev(int pos, int length) {
		pos--;
		if (pos == -1) {
			return length - 1;
		}
		return pos;
	}

	public static int randomDistributed(double[] probabSums, IRandom random) {
		int n = probabSums.length;
		double ch = random.get() * probabSums[n - 1];
		for (int i = 0; i < n; i++) {
			if (ch <= probabSums[i]) {
				return i;
			}
		}
		return n - 1;
	}

	@SuppressWarnings("unchecked")
	public static Object extractRandomFromList(List list, IRandom random) {
		int pos = randomInt(random, list.size());
		Object res = list.get(pos);
		list.remove(pos);
		return res;
	}
}
